use std::ops::DerefMut;

use serial_port::SerialPort;
use tauri::{command, Window};
use tokio::sync::Mutex;

use crate::error::AppErr;

mod serial_port;

static SERIAL_PORT: Mutex<Option<SerialPort>> = Mutex::const_new(None);

#[command]
pub fn available_ports() -> Result<Vec<String>, AppErr> {
    let ports = tokio_serial::available_ports()?;
    let ports: Vec<String> = ports.into_iter().map(|port| port.port_name).collect();
    Ok(ports)
}

#[command]
pub async fn open(name: String, baud_rate: u32, window: Window) -> Result<(), AppErr> {
    let mut lock = SERIAL_PORT.lock().await;
    if let Some(p) = lock.as_ref() {
        p.close();
    }

    let port = SerialPort::new(&name, baud_rate, window)?;
    *lock = Some(port);
    Ok(())
}

#[command]
pub async fn close() -> Result<(), AppErr> {
    let mut lock = SERIAL_PORT.lock().await;

    match lock.as_ref() {
        Some(p) => {
            p.close();
            *lock = None;
        }
        None => {}
    };

    Ok(())
}

#[command]
pub async fn write(data: Box<[u8]>) -> Result<(), AppErr> {
    let mut lock = SERIAL_PORT.lock().await;
    if let Some(port) = lock.deref_mut() {
        port.write(&data).await?;
    } else {
        return Err(AppErr::Custom("串口没有打开"));
    }

    Ok(())
}

#[command]
pub async fn is_open() -> bool {
    SERIAL_PORT.lock().await.is_some()
}
